<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=565541
-->
<head>
  <title>Test for Bug 565541</title>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=565541">Mozilla Bug 565541</a>
<p id="display"></p>
<div id="content" style="display: none">
  
</div>
<pre id="test">
<script type="application/javascript">

/** Test for Bug 565541 **/

SimpleTest.waitForExplicitFinish();

var previousX, previousY, previousWidth, previousHeight;

["innerWidth", "innerHeight", "screenX", "screenY", "outerWidth",
 "outerHeight"].map(function(name) {
  window[name+"Getter"] = Object.getOwnPropertyDescriptor(window, name).get;
});

function backValues()
{
  previousX = window.screenX;
  previousY = window.screenY;
  previousWidth = window.innerWidth;
  previousHeight = window.innerHeight;
}

function restoreValues()
{
  window.screenX = previousX;
  window.screenY = previousY;
  window.innerWidth = previousWidth;
  window.innerHeight = previousHeight;
}

function getNewWidth(aWindow)
{
  return (aWindow.innerWidth > (screen.width / 2)) ? 100 : screen.width;
}

function getNewHeight(aWindow)
{
  return (aWindow.innerHeight > (screen.height / 2)) ? 100 : screen.height;
}

function getNewX(aWindow)
{
  return (aWindow.screenX > ((screen.width - aWindow.outerWidth) / 2))
    ? 0 : screen.width - aWindow.outerWidth;
}

function getNewY(aWindow)
{
  return (aWindow.screenY > ((screen.height - aWindow.outerHeight) / 2))
    ? 0 : screen.height - aWindow.outerHeight;
}

/**
 * hitEventLoop is called when we want to check something but we can't rely on
 * an event or a specific number of event loop hiting.
 * This method can be called by specifying a condition, a test (using SimpleTest
 * API), how many times the event loop has to be hitten and what to call next.
 * If times < 0, the event loop will be hitten as long as the condition isn't
 * true or the test doesn't time out.
 */
function hitEventLoop(condition, test, times) {
  return new Promise(function(resolve, reject) {
    function doMagic() {
      if (condition() || times == 0) {
        test();
        resolve();
        return;
      }

      setTimeout(doMagic, 0, condition, test, times - 1);
    }

    doMagic();
  });
}

function checkChangeIsDisabled(aWindow) {
  // We want to check that nothing has changed. Having a high value would take
  // too much time. Worse thing that could happen is random green.
  var hits = 5;

  function getProp(propName) {
    return window[propName + "Getter"].call(aWindow);
  }

  var originalWidth = getProp("innerWidth");
  var originalHeight = getProp("innerHeight");

  var originalX = getProp("screenX");
  var originalY = getProp("screenY");

  var oWidth = getProp("outerWidth");
  var oHeight = getProp("outerHeight");

  function changeCondition() {
    return aWindow.innerWidth != originalWidth ||
           aWindow.innerHeight != originalHeight ||
           aWindow.screenX != originalX || aWindow.screenY != originalY ||
           aWindow.outerWidth != oWidth || aWindow.outerHeight != oHeight;
  }

  function changeTest() {
    is(getProp("innerWidth"), originalWidth,
       "Window width shouldn't have changed");
    is(getProp("innerHeight"), originalHeight,
       "Window height shouldn't have changed");
    is(getProp("screenX"), originalX,
       "Window x position shouldn't have changed");
    is(getProp("screenY"), originalY,
       "Window y position shouldn't have changed");
    is(getProp("outerWidth"), oWidth,
       "Window outerWidth shouldn't have changed");
    is(getProp("outerHeight"), oHeight,
       "Window outerHeight shouldn't have changed");
  }

  /**
   * Size changes.
   */
  var newWidth = getNewWidth(aWindow);
  var newHeight = getNewHeight(aWindow);

  aWindow.innerWidth = newWidth;
  aWindow.innerHeight = newHeight;

  aWindow.resizeTo(newWidth, newHeight);

  aWindow.resizeBy(newWidth - aWindow.innerWidth,
                   newHeight - aWindow.innerHeight);

  aWindow.sizeToContent();

  /**
   * Position checks.
   */
  var newX = getNewX(aWindow);
  var newY = getNewY(aWindow);

  aWindow.screenX = newX;
  aWindow.screenY = newY;

  aWindow.moveTo(newX, newY);

  aWindow.moveBy(newX - aWindow.screenX,
                 newY - aWindow.screenY);

  /**
   * Outer width/height checks.
   */
  aWindow.outerWidth *= 2;
  aWindow.outerHeight *= 2;

  // We did a lot of changes. Now, we are going to wait and see if something
  // happens.
  // NOTE: if this happens to fail, you will have to check manually which
  // operation has been accepted.
  return hitEventLoop(changeCondition, changeTest, hits);
}

function checkChangeIsEnabled(aWindow, aNext)
{
  // Something should happen. We are not going to go to the next test until
  // it does.
  var hits = -1;

  var prevWidth;
  var prevHeight;

  var prevX;
  var prevY;

  var oWidth;
  var oHeight;

  function sizeChangeCondition() {
    return aWindow.innerWidth != prevWidth && aWindow.innerHeight != prevHeight;
  }

  function sizeChangeTest() {
    isnot(aWindow.innerWidth, prevWidth, "Window width should have changed");
    isnot(aWindow.innerHeight, prevHeight, "Window height should have changed");

    prevWidth = aWindow.innerWidth;
    prevHeight = aWindow.innerHeight;
  }

  function posChangeCondition() {
    // With GTK, sometimes, only one dimension changes.
    if (navigator.platform.indexOf('Linux') != -1) {
      return aWindow.screenX != prevX || aWindow.screenY != prevY;
    }
    return aWindow.screenX != prevX && aWindow.screenY != prevY;
  }

  function posChangeTest() {
    // With GTK, sometimes, only one dimension changes.
    if (navigator.platform.indexOf('Linux') != -1) {
      // With GTK, sometimes, aWindow.screenX changes during two calls.
      // So we call it once and save the returned value.
      var x = aWindow.screenX;
      var y = aWindow.screenY;
      if (x != prevX) {
        isnot(x, prevX, "Window x position should have changed");
      }
      if (y != prevY) {
        isnot(y, prevY, "Window y position should have changed");
      }
    } else {
      isnot(aWindow.screenX, prevX, "Window x position should have changed");
      isnot(aWindow.screenY, prevY, "Window y position should have changed");
    }

    prevX = aWindow.screenX;
    prevY = aWindow.screenY;
  }

  function outerChangeCondition() {
    return aWindow.outerWidth != oWidth && aWindow.outerHeight != oHeight;
  }

  function outerChangeTest() {
    isnot(aWindow.outerWidth, oWidth, "Window outerWidth should have changed");
    isnot(aWindow.outerHeight, oHeight, "Window outerHeight should have changed");
  }

  /**
   * Size checks.
   */
  prevWidth = aWindow.innerWidth;
  prevHeight = aWindow.innerHeight;
  aWindow.innerWidth = getNewWidth(aWindow);
  aWindow.innerHeight = getNewHeight(aWindow);

  hitEventLoop(sizeChangeCondition, sizeChangeTest, hits)
  .then(function() {
    aWindow.resizeTo(getNewWidth(aWindow), getNewHeight(aWindow));
    return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
  })
  .then(function () {
    aWindow.resizeBy(getNewWidth(aWindow) - aWindow.innerWidth,
                     getNewHeight(aWindow) - aWindow.innerHeight);
    return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
  })
  .then(function() {
    aWindow.sizeToContent();
    return hitEventLoop(sizeChangeCondition, sizeChangeTest, hits);
  })
  .then(function() {
    /**
     * Position checks.
     */
    prevX = aWindow.screenX;
    prevY = aWindow.screenY;

    aWindow.screenX = getNewX(aWindow);
    aWindow.screenY = getNewY(aWindow);
    return hitEventLoop(posChangeCondition, posChangeTest, hits);
  })
  .then(function() {
    prevX = aWindow.screenX;
    prevY = aWindow.screenY;

    aWindow.moveTo(getNewX(aWindow), getNewY(aWindow));
    return hitEventLoop(posChangeCondition, posChangeTest, hits);
  })
  .then(function() {
    prevX = aWindow.screenX;
    prevY = aWindow.screenY;

    aWindow.moveBy(getNewX(aWindow) - aWindow.screenX,
                   getNewY(aWindow) - aWindow.screenY);
    return hitEventLoop(posChangeCondition, posChangeTest, hits);
  })
  .then(function() {
    /**
     * Outer width/height checks.
     */
    oWidth = aWindow.outerWidth;
    oHeight = aWindow.outerHeight;

    aWindow.outerWidth = oWidth * 2;
    aWindow.outerHeight = oHeight * 2;
    return hitEventLoop(outerChangeCondition, outerChangeTest, hits);
  })
  .then(function() {
    let origWidth = oWidth;
    let origHeight = oHeight;

    oWidth = aWindow.outerWidth;
    oHeight = aWindow.outerHeight;

    aWindow.outerWidth = origWidth;
    aWindow.outerHeight = origHeight;
    return hitEventLoop(outerChangeCondition, outerChangeTest, hits);
  })
  .then(aNext);
}

SpecialPowers.pushPrefEnv({
  "set": [["dom.disable_window_move_resize", false],
          ["security.data_uri.block_toplevel_data_uri_navigations", false],]},
                          function() {
SimpleTest.waitForFocus(function() {
  if (screen.width <= 200 || screen.height <= 200) {
    todo(false, "The screen needs to be bigger than 200px*200px to run this test.");
    SimpleTest.finish();
    return;
  }

  backValues();

  // The current window can't change it's own size and position.
  checkChangeIsDisabled(window).then(function() {
    // We create a window and check that it can change its own size and position.
    // However, passing size/position parameters to window.open should work.
    var w = window.open("data:text/html,<script>" +
      "function check(next) {" +
      "  var is_range = function(aTest, aValue, aRange, aMsg) {" +
      "    window.opener.ok(aTest < aValue + aRange && aTest > aValue - aRange, aMsg);" +
      "  };" +
      "  is_range(window.innerWidth, 170, 5, 'parameter width should be taken into account');" +
      "  is_range(window.innerHeight, 170, 5, 'parameter height should be taken into account');" +
      "  is_range(window.screenX, 65, 5, 'parameter screenX should be taken into account');" +
      "  is_range(window.screenY, 65, 5, 'parameter screenY should be taken into account');" +
      "  window.opener.checkChangeIsEnabled(window, next);" +
      "} <\/script>", '',
      'width=170,height=170,screenX=65,screenY=65');

    SimpleTest.waitForFocus(function() {
      w.check(function() {
        // The current window can change the size and position of the created one.
        checkChangeIsEnabled(w, function() {
          w.close();

          // If we call window.open with an empty string as a third parameter,
          // by default, it will create a new tab instead of a new window.
          // In that case, we shouldn't allow the caller to change the size/position.
          w = window.open("data:text/html,<script>" +
            "function check(next) {" +
            "  window.opener.checkChangeIsDisabled(window).then(next);" +
            "} <\/script>", '', '');

          SimpleTest.waitForFocus(function() {
            w.check(function() {

              // The current window can't change the size and position of the new tab.
              checkChangeIsDisabled(w).then(function() {
                w.close();

                restoreValues();
                SimpleTest.finish();
              });
            });
          }, w, false);
        });
      })
    }, w, false);
  });
});
}); // SpecialPowers.pushPrefEnv()

</script>
</pre>
</body>
</html>
